// Copyright (c) 2011 The Kiwii Authors. All rights reserved.
// Use of this source code is governed by the New BSD License that can be
// found in the LICENSE file. See the AUTHORS file for names of contributors.

#include "comm/Kiwii.hxx"
#include "comm/Protocol.hxx"
#include "comm/Exception.hxx"
#include "comm/Logger.hxx"
#include <boost/lexical_cast.hpp>
#include <sstream>

namespace kiwii {
  namespace comm {
    namespace protocol {

      static Logger& logger = Logger::get();

      class RequestIncomplete : public Exception {
      public:
	RequestIncomplete(std::string const& e = "") : Exception("Request incomplete: " + e) {}
      };

      class RequestInvalid : public Exception {
      public:
	RequestInvalid(std::string const& e = "") : Exception("Request invalid: " + e) {}
      };

      static long long getLength(char const*& p, std::size_t size) {
	auto start = p, end = p + size;
	for (; p < end; p++) {
	  if (*p == '\r') {
	    if (p + 1 >= end) {
	      throw RequestIncomplete();
	    }
	    if (*(p + 1) == '\n') {
	      try {
		p += 2;
		return boost::lexical_cast<long long>(std::string(start, p - 2));
	      } catch (boost::bad_lexical_cast& e) {
		printf("%s\n", p);
		throw RequestInvalid(e.what());
	      }
	    }
	  }
	}
	throw RequestIncomplete();
      }

      Request::Request(char const* buf, std::size_t size) : ok_(false) {
	auto start = buf;
	try {
	  if (buf[0] != '*') {
	    throw RequestInvalid(std::string("error request identifier: ") + buf[0]);
	  }

	  auto end = buf + size;
	  auto bulkNum = getLength(++buf, size);
	  while (bulkNum-- > 0) {
	    if (buf < end) {
	      if (*buf != '$') {
		throw RequestInvalid(std::string("error bulk delimiter: ") + *buf); 
	      }
	      auto len = getLength(++buf, size);
	      if ((buf + len + 2) <= end) {
		args_.push_back(std::string(buf, len));
		buf += (len + 2);
	      } else {
		throw RequestIncomplete();
	      }
	    } else {
	      throw RequestIncomplete();
	    }
	  }
	  ok_ = true;
	  bytes_ = buf - start; 
	} catch (RequestIncomplete& e) {
	  bytes_ = 0;
	  args_.clear();
	} catch (RequestInvalid& e) {
	  bytes_ = size;
	  args_.clear();
	  logger. error("%s\n%s", e.what(), std::string(start, size).c_str());
	}
      }

    }
  }
}
